//
//  YZMCustomURLProtocol.m
//  YZMicroStock
//
//  Created by Lv.Dongdong on 2017/3/23.
//  Copyright © 2017年 cqjr. All rights reserved.
//

#import "YZMCustomURLProtocol.h"
#import "Util.h"

static NSString * const hasInitKey = @"YZDCustomWebViewProtocolKey";

@interface YZMCustomURLProtocol ()

/**
 返回的数据
 */
@property (nonatomic, strong) NSMutableData *responseData;

/**
 网络
 */
@property (nonatomic, strong) NSURLConnection *connection;

/**
 路径
 */
@property (nonatomic, copy) NSString * diskPath;

/**
 缓存时间
 */
@property (nonatomic, assign) NSInteger cacheTime;

/**
 服务器返回的响应
 */
@property (nonatomic, strong) NSURLResponse *response;

/**
 data路径
 */
@property (nonatomic, copy) NSString *filePath;

/**
 header信息
 */
@property (nonatomic, copy) NSString *otherInfoPath;

/**
 修改后的请求
 */
@property (nonatomic, strong) NSMutableURLRequest *mutableRequest;

@end

@implementation YZMCustomURLProtocol

/**
 初始化
 
 @param request
 @param cachedResponse
 @param client
 @return
 */
- (instancetype)initWithRequest:(NSURLRequest *)request cachedResponse:(NSCachedURLResponse *)cachedResponse client:(id<NSURLProtocolClient>)client {
    
    if (self = [super initWithRequest:request cachedResponse:cachedResponse client:client]) {
        
        self.cacheTime = 3600;
        self.diskPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
    }
    
    return self;
}

/**
 protocol入口
 
 @param request 被拦截的请求
 @return YES：处理 or NO：不处理
 */
+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
    
    //需要拦截请求中包含的字符集合
    NSSet *schemeSet = [NSSet setWithArray:@[@"/app/css/", @"/app/lib/", @"/app/js/", @"/app/partials/", @"/app/index.html", @".jpt", @".png"]];
    
    for (NSString *contentStr in schemeSet) {
        
        //只处理包含schemeSet中字符的请求，如果已处理过则不做处理
        if ([request.URL.path containsString:contentStr] &&
            ![NSURLProtocol propertyForKey:hasInitKey inRequest:request]) {
            NSLog(@"request:%@", request.URL);
            return YES;
        }
    }
    
    return NO;
}

/**
 处理可request的方法
 
 @param request 被拦截的请求
 @return 处理后的请求
 */
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
    
    //处理后的request
    NSMutableURLRequest *mutableRequest = [YZMCustomURLProtocol changeRequest:request];
    
    return mutableRequest;
}

/**
 请求发起
 */
- (void)startLoading {
    
    //处理后的request
    self.mutableRequest = [YZMCustomURLProtocol changeRequest:self.request];
    self.mutableRequest.cachePolicy = NSURLRequestReloadIgnoringLocalAndRemoteCacheData;
    NSString *fileName = [self cacheRequestFileName:self.mutableRequest.URL.absoluteString];
    NSString *otherInfoFileName = [self cacheRequestOtherInfoFileName:self.mutableRequest.URL.absoluteString];
    //缓存文件路径
    self.filePath = [self cacheFilePath:fileName];
    //缓存其它信息文件的路径
    self.otherInfoPath = [self cacheFilePath:otherInfoFileName];
    NSDate *date = [NSDate date];
    //做下标记，防止递归调用
    [NSURLProtocol setProperty:@YES forKey:hasInitKey inRequest:self.mutableRequest];
    
    if (!kNetworkNotReachability) {
        
        //判断沙盒中是否缓存了相应请求的数据
        if ([[NSFileManager defaultManager] fileExistsAtPath:self.filePath]) {
            
            BOOL expire = NO;
            NSDictionary *otherInfo = [NSDictionary dictionaryWithContentsOfFile:self.otherInfoPath];
            if (self.cacheTime > 0) {
                NSInteger createTime = [otherInfo[@"time"] integerValue];
                //判断本地文件是否过期
                if (createTime + self.cacheTime < [date timeIntervalSince1970]) {
                    
                    expire = YES;
                }
            }
            expire = YES;
            if (expire == NO) {
                
                //读取本地缓存
                [self readLocalCache];
            } else {
                
                //保存时间过期，重新请求
                [self.mutableRequest setValue:otherInfo[@"lastModified"] forHTTPHeaderField:@"If-Modified-Since"];
                //重新请求
                self.connection = [NSURLConnection connectionWithRequest:self.mutableRequest delegate:self];
            }
            
        } else {
            
            //请求
            self.connection = [NSURLConnection connectionWithRequest:self.mutableRequest delegate:self];
        }
    } else {
        
        [self readLocalCache];
    }
}

/**
 请求结束
 */
- (void)stopLoading {
    
    [self.connection cancel];
    self.connection = nil;
}

#pragma mark -- NSURLConnectionDelegate

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    
    [self.client URLProtocol:self didFailWithError:error];
}

#pragma mark -- NSURLConnectionDataDelegate

/**
 接收到服务器响应
 
 @param connection
 @param response 响应信息
 */
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    self.responseData = [[NSMutableData alloc] init];
    self.response = [response copy];
}

/**
 服务器返回的数据
 
 @param connection
 @param data 数据
 */
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    
    [self.responseData appendData:data];
}

/**
 完成加载
 
 @param connection
 */
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    
    NSDate *date = [NSDate date];
    
    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)self.response;
    NSLog(@"httpResponse_set:%@", httpResponse);
    if (httpResponse.statusCode == 304) {
        
        //保存时间示过期，直接从沙盒中读取
        [self readLocalCache];
    } else {
        
        //将服务器响应的信息保存成字典，方便下次从本地读取
        NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"%f", [date timeIntervalSince1970]], @"time", self.response.MIMEType, @"MIMEType", httpResponse.allHeaderFields[@"Last-Modified"], @"lastModified", self.response.textEncodingName, @"textEncodingName", nil];
        
        if (dict) {
            
            [dict writeToFile:self.otherInfoPath atomically:YES];
        }
        if (self.responseData.length > 0) {
            
            [self.responseData writeToFile:self.filePath atomically:YES];
        }
        
        [self.client URLProtocol:self didReceiveResponse:self.response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
        [self.client URLProtocol:self didLoadData:self.responseData];
        [self.client URLProtocolDidFinishLoading:self];
    }
}

#pragma mark -- private

/**
 保存时间示过期，直接从沙盒中读取
 */
- (void)readLocalCache {
    
    NSDictionary *otherInfo = [NSDictionary dictionaryWithContentsOfFile:self.otherInfoPath];
    
    if (otherInfo) {
        
        //保存时间示过期，直接从沙盒中读取
        NSData *data = [NSData dataWithContentsOfFile:self.filePath];
        NSURLResponse *response = [[NSURLResponse alloc] initWithURL:self.mutableRequest.URL
                                                            MIMEType:[otherInfo objectForKey:@"MIMEType"]
                                               expectedContentLength:data.length
                                                    textEncodingName:[otherInfo objectForKey:@"textEncodingName"]];
        
        [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
        [self.client URLProtocol:self didLoadData:data];
        [self.client URLProtocolDidFinishLoading:self];
    }
}

/**
 修改请求
 
 @param request 原请求
 @return 修改后的请求
 */
+ (NSMutableURLRequest *)changeRequest:(NSURLRequest *)request {
    
    //将不可变request改为可变
    NSMutableURLRequest *mutableRequest = [request mutableCopy];
    //删除URL中的随机数
    NSString *url = [mutableRequest.URL.absoluteString stringByReplacingOccurrencesOfString:@"[?&]v=\\d+" withString:@"" options:NSRegularExpressionSearch range:NSMakeRange(0, mutableRequest.URL.absoluteString.length)];
    mutableRequest.URL = [NSURL URLWithString:url];
    
    return mutableRequest;
}

/**
 目录名
 
 @return 目录名
 */
- (NSString *)cacheFolder {
    
    return @"URLCache";
}

/**
 路径
 
 @param file 文件名
 @return 路径
 */
- (NSString *)cacheFilePath:(NSString *)file {
    
    NSString *path = [NSString stringWithFormat:@"%@/%@", self.diskPath, [self cacheFolder]];
    NSFileManager *fileManager = [NSFileManager defaultManager];
    BOOL isDir;
    if ([fileManager fileExistsAtPath:path isDirectory:&isDir] && isDir) {
        
    } else {
        [fileManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];
    }
    return [NSString stringWithFormat:@"%@/%@", path, file];
}

/**
 路径
 
 @param requestUrl URL
 @return MD5后的字符
 */
- (NSString *)cacheRequestFileName:(NSString *)requestUrl {
    
    return [Util md5Hash:requestUrl];
}

/**
 路径
 
 @param requestUrl URL
 @return MD5后的字符
 */
- (NSString *)cacheRequestOtherInfoFileName:(NSString *)requestUrl {
    
    return [Util md5Hash:[NSString stringWithFormat:@"%@-otherInfo", requestUrl]];
}

@end
